#include "stdafx.h"

#include "MxUtils\MxError1.h"
#include "..\..\PublicInterfaces\PluginICPopenCV\PluginICPopenCVErrorCodes.h"
#include "PluginICPopenCV.h"

#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\imgproc\imgproc_c.h>
#include <opencv2\highgui\highgui.hpp>	//needed for imread(), imwrite()

#include "Utils.h"
#include "GaussianImage.h"


GaussianImage::GaussianImage(void):
	_refCnt(0)
{
}


GaussianImage::~GaussianImage(void)
{
}

bool		MXSTDMETHOD GaussianImage::Invoke(IImageComputeFn *fn, bool DebugMode, bool DisplayMsg)
{
	bool rc = false; 

		if ((fn == nullptr) || (fn->IsValid() == false))
		MX_SETERROR( MX11166, MxError::InvalidParams,  MxError::Abort,  MxError::VerboseReport, "fn is nullptr, or invalid");
	else
	{
		if (   (fn->IsExistParam("StopProc", IImageComputeFnDef::ParamType::Bool, IImageComputeFnDef::Direction::IO) == false)
			|| (fn->IsExistParam("RootFolder", IImageComputeFnDef::ParamType::String, IImageComputeFnDef::Direction::Input) == false)
			|| (fn->IsExistParam("IJC", IImageComputeFnDef::ParamType::String, IImageComputeFnDef::Direction::Input) == false)
			|| (fn->IsExistParam("IJF", IImageComputeFnDef::ParamType::String, IImageComputeFnDef::Direction::Input) == false)
			|| (fn->IsExistParam("ImageRef", IImageComputeFnDef::ParamType::String, IImageComputeFnDef::Direction::IO) == false)
			|| (fn->IsExistParam("KernelHt", IImageComputeFnDef::ParamType::Integer, IImageComputeFnDef::Direction::Input) == false)
			|| (fn->IsExistParam("KernelWd", IImageComputeFnDef::ParamType::Integer, IImageComputeFnDef::Direction::Input) == false)
			|| (fn->IsExistParam("SigmaX", IImageComputeFnDef::ParamType::Double, IImageComputeFnDef::Direction::Input) == false)
			|| (fn->IsExistParam("SigmaY", IImageComputeFnDef::ParamType::Double, IImageComputeFnDef::Direction::Input) == false)
			|| (fn->IsExistParam("Border", IImageComputeFnDef::ParamType::Integer, IImageComputeFnDef::Direction::Input) == false))
			MX_SETERROR( MX11167, MxError::BadUserInput,  MxError::Abort,  MxError::VerboseReport, "IJCFolder, ImageFolder, ImageRef, KernelWd, KernelHt, SigmaX, SigmaY, Border are not supported by this fn");
		else
		{
			if (fn->GetParamBool("StopProc") == true)
			{
				rc = true;
			}
			else
			{
				std::string imageRefDst("");
				std::string imageRef(fn->GetParamStr("ImageRef"));
				std::string pathfilenameSrcImage("");
				std::string pathfilenameDstImage("");
				std::string pathfilenameDebugImage("");

				if (PluginOpenCV::Utils::GetImagePathFilenames(fn, fn->GetParamStr("RootFolder"), fn->GetParamStr("IJC"), fn->GetParamStr("IJF"), imageRef.c_str(),
					fn->GetFileType(), fn->GetFileType(), //if function changes the filetype (.bmp to .jpg) then add to function params the std IO variable ImageProcFilev1::VariableFileTypeName and set it to new filetype before returning - see Job::Run()
					&imageRefDst, &pathfilenameSrcImage, &pathfilenameDstImage, &pathfilenameDebugImage) == false)
					MX_SETERROR(MX11195, MxError::CodeDefect, MxError::Abort, MxError::VerboseReport, "GetImagePathFilenames failed for step=%d", fn->GetSeqNo());
				else
				{
					if (SmoothGaussian(pathfilenameSrcImage.c_str(), pathfilenameDstImage.c_str(),
						fn->GetParamInt("KernelHt"), fn->GetParamInt("KernelWd"),
						fn->GetParamDouble("SigmaX"), fn->GetParamDouble("SigmaY"),
						fn->GetParamInt("Border")) == true)
					{
						if (fn->SetParamStr("ImageRef", imageRefDst.c_str()) == false)
							MX_SETERROR(MX11241, MxError::CodeDefect, MxError::Abort, MxError::QuietReport, "SetParamStr(ImageRef, %s) failed for image %s", imageRefDst.c_str(), imageRef.c_str());
						else
						{
							rc = true;
						}
					}
				}
			}
		}
	}
	return rc;
}

bool		MXSTDMETHOD GaussianImage::SmoothGaussian(const char *srcImagePathFilename, const char *destImagePathFilename, int KernelHt, int KernelWd, double SigmaX, double SigmaY, int Border )
{
	bool rc = false;

	if ((srcImagePathFilename == nullptr) || (destImagePathFilename == nullptr) || (KernelHt < 0 ) || (KernelWd < 0) || (SigmaX < 0.0) || (SigmaY < 0.0) || (Border < 0) || (Border > 5))
		MX_SETERROR( MX11168, MxError::InvalidParams,  MxError::Abort,  MxError::VerboseReport, "one or more params is invalid");
	else
	{
		try
		{
			cv::Mat src = cv::imread(srcImagePathFilename,CV_LOAD_IMAGE_ANYDEPTH);
			if (src.data == nullptr)
				MX_SETERROR(MX11169, MxError::BadUserInput,  MxError::Abort,  MxError::VerboseReport, "unable to open source image=%s", srcImagePathFilename);
			else
			{	
				cv::Mat  dst = cv::Mat::zeros( src.size(), src.type() );
				cv::Size ksize(KernelWd, KernelHt);
				cv::GaussianBlur(src, dst, ksize, SigmaX, SigmaY, PluginOpenCV::Utils::Convert2BorderType(Border) );

				if (cv::imwrite(destImagePathFilename, dst) == false)
					MX_SETERROR(MX11170, MxError::BadUserInput,  MxError::Abort,  MxError::VerboseReport, "unable to write dest image=%s", destImagePathFilename);
				else
				{
					rc = true;
				}
			}
		}
		catch(std::exception& e)
		{
			std::string result("[exception msg not found]");
			MX_SETERROR( MX11190, MxError::CodeDefect,  MxError::Abort,  MxError::QuietReport, "threw exception %s for source image=%s", PluginICPopenCV::GetOpenCVExceptionMsg(e, &result), srcImagePathFilename);
		}
	}
	return rc;
}

long		MXSTDMETHOD GaussianImage::DestroyPtr(void)
{
	long cnt = InterlockedDecrement(&_refCnt);
	if ( cnt == 0 )
		delete this;
	return cnt;
}

long		MXSTDMETHOD GaussianImage::DuplicatePtr(void)
{
	return InterlockedIncrement(&_refCnt);
}

void *		MXSTDMETHOD GaussianImage::Dynamic_cast(const GUID IId)
{
	void *rc = nullptr;

	if ( MxGUID::IsEqual(IId, IMxPluginBaseIID::Guid()))
		rc = static_cast<IMxPluginBase *>(this);
	else
		rc = nullptr;

	std::string tmp;
	if (rc == nullptr)
		MX_SETERROR(MX11165, MxError::Install, MxError::Abort, MxError::QuietReport, "Dynamic_cast does not support IID=%s", MxGUID::ConvertToString(IId, &tmp));
	else
		((IMxPluginBase *)rc)->DuplicatePtr();

	return rc;
}
